"""XXX
"""

class LdapCallbackRegistry(object):

    def __init__(self):
        self._callbacks = []
        self._pre_add_callbacks = []
        self._pre_rename_callbacks = []
        self._pre_modify_callbacks = []
        self._pre_delete_callbacks = []
        self._post_add_callbacks = []
        self._post_rename_callbacks = []
        self._post_modify_callbacks = []
        self._post_delete_callbacks = []

    def register(self, callback):
        for callback_type in ('add', 'rename', 'modify', 'delete'):
            for what in ('pre', 'post'):
                method = getattr(callback, what+'_'+callback_type)
                class_meth = getattr(LdapCallback, what+'_'+callback_type)
                if method is not class_meth:
                    ls = getattr(self, '_'+what+'_'+callback_type+'_callbacks')
                    ls.append(callback)

    ##########################################################################

    def pre_add(self, model, modlist, extra_callback=None):
        for callback in self._pre_add_callbacks:
            callback.pre_add(model, modlist)
        if extra_callback is not None:
            extra_callback.pre_add(model, modlist)

    def pre_rename(self, model, original_dn, new_dn, extra_callback=None):
        for callback in self._pre_rename_callbacks:
            callback.pre_rename(model, original_dn, new_dn)
        if extra_callback is not None:
            extra_callback.pre_rename(model, original_dn, new_dn)

    def pre_modify(self, model, modlist, extra_callback=None):
        for callback in self._pre_modify_callbacks:
            callback.pre_modify(model, modlist)
        if extra_callback is not None:
            extra_callback.pre_modify(model, modlist)

    def pre_delete(self, model, extra_callback=None):
        for callback in self._pre_delete_callbacks:
            callback.pre_delete(model)
        if extra_callback is not None:
            extra_callback.pre_delete(model)

    ##########################################################################

    def post_add(self, model, modlist, extra_callback=None):
        for callback in self._post_add_callbacks:
            callback.post_add(model, modlist)
        if extra_callback is not None:
            extra_callback.post_add(model, modlist)

    def post_rename(self, model, original_dn, new_dn, extra_callback=None):
        for callback in self._post_rename_callbacks:
            callback.post_rename(model, original_dn, new_dn)
        if extra_callback is not None:
            extra_callback.post_rename(model, original_dn, new_dn)

    def post_modify(self, model, modlist, extra_callback=None):
        for callback in self._post_modify_callbacks:
            callback.post_modify(model, modlist)
        if extra_callback is not None:
            extra_callback.post_modify(model, modlist)

    def post_delete(self, model, extra_callback=None):
        for callback in self._post_delete_callbacks:
            callback.post_delete(model)
        if extra_callback is not None:
            extra_callback.post_delete(model)

_registry = LdapCallbackRegistry()

class LdapCallback(object):

    """XXX

    The ``modlist`` parameters are *similar* to that is used in the Python LDAP
    library.  However, the ``modify`` operation is slightly different.

    Add Modlist
    ===========
    The add modlist is a list of ``(attribute_name, value)`` tuples.  The value
    will be a string for single-value attributes, or a list.  This is the same
    as the Python LDAP API.

    Modify Modlist
    ==============
    The modify modlist is slightly different from the Python LDAP API.  It also
    includes the old value for your convenience.  It is a list of ``(mod_op,
    attribute_name, old_value, new_value)`` tuples.  The ``mod_op`` is one of
    ``ldap.MOD_ADD``, ``ldap.MOD_DELETE``, or ``ldap.MOD_REPLACE``.
    ``old_value`` will be None if it was not previously set, and conversely
    ``new_value`` will be None if the attribute is being deleted.
    """

    def register(self):
        """Register this callback."""
        _registry.register(self)

    def pre_add(self, model, modlist):
        """Called just before an object is added.

        :Parameters:
            - `model`: The model instance.
            - `modlist`: The attribute modification list.  List of
              ``(attribute_name, value)`` tuples.
        """
        return

    def pre_rename(self, model, original_dn, new_dn):
        """Called just before an object is renamed.

        :Parameters:
            - `model`: The model instance.
            - `original_dn`: The original dn.
            - `new_dn`: The new dn.
        """
        return

    def pre_modify(self, model, modlist):
        """Called just before an object is modified.

        :Parameters:
            - `model`: The model instance.
            - `modlist`: The attribute modification list.  List of ``(mod_op,
              attribute_name, old_value, new_value)`` tuples.
        """
        return

    def pre_delete(self, model):
        """Called just before an object is deleted.

        :Parameters:
            - `model`: The model instance.
        """
        return

    ##########################################################################

    def post_add(self, model, modlist):
        """Called just after an object is added.

        :Parameters:
            - `model`: The model instance.
            - `modlist`: The attribute modification list.  List of
              ``(attribute_name, value)`` tuples.
        """
        return

    def post_rename(self, model, original_dn, new_dn):
        """Called just after an object is renamed.

        :Parameters:
            - `model`: The model instance.
            - `original_dn`: The original dn.
            - `new_dn`: The new dn.
        """
        return

    def post_modify(self, model, modlist):
        """Called just after an object is modified.

        :Parameters:
            - `model`: The model instance.
            - `modlist`: The attribute modification list.  List of ``(mod_op,
              attribute_name, old_value, new_value)`` tuples.
        """
        return

    def post_delete(self, model):
        """Called just after an object is deleted.

        :Parameters:
            - `model`: The model instance.
        """
        return
